#include "log.hpp"
#include "threadpool.hpp"
#include "protocol.hpp"

#include <iostream>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <pthread.h>
#include <stdlib.h>
#include <signal.h>
#include <arpa/inet.h>
#include <string.h>

using namespace std;

class tcp;
class threadData
{
public:
    string clientip_;
    uint16_t clientport_;
    int sockfd_;
    tcp *ts_;

public:
    threadData(string clientip, uint16_t clientport, int sockfd, tcp *ts) : clientip_(clientip),
                                                                            clientport_(clientport), sockfd_(sockfd), ts_(ts)
    {
    }
};

void service(int servicefd, string clientip, uint16_t clientport)
{
    // 2.1、获取客户发送的数据
    // 数据流使用write和read
    while (true)
    {
        char buffer[1024];
        ssize_t s = read(servicefd, buffer, sizeof(buffer) - 1);
        if (s > 0)
        {
            buffer[s] = 0;
            if (strcasecmp(buffer, "quit") == 0)
            {
                LogMessage(DEBUG, "service[%s][%d]:quit1", clientip.c_str(), clientport);
                break;
            }
            LogMessage(NOTICE, "service[%s][%d]:transbefor:%s", clientip.c_str(), clientport, buffer);
            for (int i = 0; i < s; i++)
            {
                if (isalpha(buffer[i]) && islower(buffer[i]))
                {
                    buffer[i] = toupper(buffer[i]);
                }
            }
            LogMessage(NOTICE, "service[%s][%d]:transafter:%s", clientip.c_str(), clientport, buffer);
            write(servicefd, buffer, strlen(buffer));
            // 全双工通信
        }
        else if (s == 0)
        {
            // 写端关闭
            LogMessage(DEBUG, "service[%s][%d]:quit2", clientip.c_str(), clientport);
            break;
        }
        else
        {
            LogMessage(FATAL, "read:%s:%d", strerror(errno), servicefd);
            break;
        }
    }
    close(servicefd);
    LogMessage(DEBUG, "service finish[%s][%d]", clientip.c_str(), clientport);
}

void execCommand(int servicefd, string clientip, uint16_t clientport)
{
    char command[1024];
    while (true)
    {

        ssize_t s = read(servicefd, command, sizeof(command) - 1);
        if (s > 0)
        {
            command[s] = 0;
            FILE *out = popen(command, "r");
            if (out == nullptr)
            {
                LogMessage(WARNING, "execCommand[%s][%d]:popen failed", clientip.c_str(), clientport);
                break;
            }
            char work[1024] = {0};
            while (fgets(work, sizeof(work) - 1, out) != nullptr)
            {
                write(servicefd, work, strlen(work));
            }
            pclose(out);
            LogMessage(DEBUG, "[%s:%d] exec [%s] ... done", clientip.c_str(), clientport, command);
            // 全双工通信
        }
        else if (s == 0)
        {
            // 写端关闭
            LogMessage(DEBUG, "execCommand[%s][%d]:quit2", clientip.c_str(), clientport);
            break;
        }
        else
        {
            LogMessage(FATAL, "read:%s:%d", strerror(errno), servicefd);
            break;
        }
    }
    close(servicefd);
    LogMessage(DEBUG, "execCommand finish[%s][%d]", clientip.c_str(), clientport);
}

resp caluate(const req &requst)
{
    resp response;
    switch (requst.op_)
    {
    case '+':
        response.result_ = requst.x_ + requst.y_;
        response.errcode_ = 0;
        break;
    case '-':
        response.result_ = requst.x_ - requst.y_;
        response.errcode_ = 0;
        break;
    case '*':
        response.result_ = requst.x_ * requst.y_;
        response.errcode_ = 0;
        break;
    case '/':
        if (requst.y_ == 0)
        {
            response.errcode_ = 1;
            response.result_ = 0;
        }
        else
        {
            response.result_ = requst.x_ / requst.y_;
            response.errcode_ = 0;
        }
        break;
    case '%':
        if (requst.y_ == 0)
        {
            response.errcode_ = 1;
            response.result_ = 0;
        }
        else
        {
            response.result_ = requst.x_ % requst.y_;
            response.errcode_ = 0;
        }
        break;
    default:
        response.errcode_ = 3;
        response.result_ = 0;
        break;
    }
    return response;
}

void netcal(int servicefd, string clientip, uint16_t clientport)
{
    string inbuffer;
    while (true)
    {
        char buffer[128];
        req requst;
        size_t sz = read(servicefd, buffer, sizeof(buffer) - 1);
        if (sz == 0)
        {
            break;
        }
        if (sz < 0)
        {
            break;
        }
        inbuffer += buffer;
        int len = 0;
        string package = decode(inbuffer, &len);
        if (len == 0)
        {
            // 没有读到完整报文，直接进入下一次循环，读取
            continue;
        }
        requst.deserialize(package);
        resp response = caluate(requst);
        string out;
        response.serialize(&out);
        if (response.errcode_ == 0)
        {
            cout << requst.x_ << " " << requst.op_ << " " << requst.y_ << " = " << response.result_ << endl;
            string encodein = encode(out, out.size());
            write(servicefd, encodein.c_str(), encodein.size());
        }
        else
        {
            cout<<"failed errcode: "<<response.errcode_<<endl;
        }
    }
    close(servicefd);
}

class tcp
{
public:
    tcp(uint16_t port, string ip = "", int sockfd = -1) : ip_(ip), port_(port), threadpool_(nullptr)
    {
    }
    ~tcp()
    {
    }

    // 4、多线程版本
    //  static void* Routine(void* argv)
    //  {
    //      threadData* client=(threadData*)argv;
    //      pthread_detach(pthread_self());
    //      service(client->sockfd_,client->clientip_,client->clientport_);
    //      delete(client);
    //      return nullptr;

    // }

    void init()
    {
        // 1 创建服务器端
        listensockfd_ = socket(AF_INET, SOCK_STREAM, 0);
        if (listensockfd_ == -1)
        {
            LogMessage(FATAL, "socket:%s:%d", strerror(errno), listensockfd_);
            exit(1);
        }
        else
        {
            LogMessage(NOTICE, "socket success:%d", listensockfd_);
        }
        // 2 绑定网络信息
        // 2.1 填充网络信息
        struct sockaddr_in local;
        local.sin_family = AF_INET;
        local.sin_port = htons(port_);
        local.sin_addr.s_addr = ip_.empty() ? htons(INADDR_ANY) : inet_addr(ip_.c_str());
        // 2.2 bindsockaddr_in和套接字绑定
        if (bind(listensockfd_, (const sockaddr *)&local, (socklen_t)sizeof(local)) == -1)
        {
            LogMessage(FATAL, "bind:%s:%d", strerror(errno), listensockfd_);
            exit(2);
        }
        LogMessage(DEBUG, "bind success:%d", listensockfd_);
        // 3 将socket设置监听状态，因为tcp是面向连接的（面向连接：需要先建立连接，再进行工作）
        if (listen(listensockfd_, 5) == -1)
        {
            LogMessage(FATAL, "listen:%s:%d", strerror(errno), listensockfd_);
            exit(3);
        }
        // 设置监听状态后，允许别人来连接你
        LogMessage(DEBUG, "listen success:%d", listensockfd_);
        // 加载线程池
        threadpool_ = tcp::threadpool_->getinstance();
    }
    void loop()
    {
        // 5、线程池先创建多个线程
        threadpool_->start();
        LogMessage(DEBUG, "thread pool start success, thread num: %d", threadpool_->thread_num());
        // signal(SIGCHLD, SIG_IGN);
        while (true)
        {
            // 1、获取连接，accept的返回值是一个新的sockfd
            // 传入的参数sockfd，这个套接字是用来获取新的连接（客户端来连接）
            // 返回值sockfd，这个套接字主要是为客户提供网络服务
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);
            int servicefd_ = accept(listensockfd_, (struct sockaddr *)&peer, &len);
            if (servicefd_ == -1)
            {
                LogMessage(WARNING, "accept:%s:%d", strerror(errno), listensockfd_);
                exit(1);
            }
            LogMessage(NOTICE, "accept success:%d", listensockfd_);

            // 2、进行服务
            // 提取clientip和port
            uint16_t clientport = ntohs(peer.sin_port);
            string clientip = inet_ntoa(peer.sin_addr);
            // 1、单进程版
            // service(servicefd_,clientip,clientport);
            // 2、多进程版
            // 父进程获取连接，子进程提供服务
            // 不能使用waitpid，因为父进程调用waitpid时，子进程可能还没有退出，父进程就阻塞住了
            //  int pid = fork();
            //  if(pid==0)
            //  {
            //      close(listensockfd_);
            //      service(servicefd_,clientip,clientport);
            //      exit(0);
            //  }
            //  close(servicefd_);

            // 3、多进程版本
            // 爷爷进程获取连接，孙子进程提供服务，等待父进程，孙子进程被1号进程领养，自动等待
            //  int pid = fork();
            //  if(pid==0)//爸爸进程
            //  {
            //      int id = fork();
            //      if(id==0)    //孙子进程
            //      {
            //          close(listensockfd_);
            //          service(servicefd_,clientip,clientport);
            //          exit(0);
            //      }
            //      else
            //      {
            //          exit(0);
            //      }
            //  }
            //  close(servicefd_);
            // 文件描述符泄露
            //  //孙子进程被bash收养，退出后，由系统自动回收，不会影响其他进程
            //  //爷爷进程调用waitpid时，爸爸进程肯定已经退出了，爷爷进程不会阻塞等待
            //  waitpid(pid,nullptr,0);

            // 4、多线程版本
            // 主线程获取链接，其余线程提供服务
            //  threadData* client=new threadData(clientip,clientport,servicefd_,this);
            //  pthread_t tid;
            //  pthread_create(&tid,nullptr,Routine,(void*)client);
            // 5、线程池版本
            // 先创建多个线程，主线程获取获取连接，客户端发送数据，主线程将任务放入线程池中，线程池中多个线程排队执行任务
            // 线程池中的多个线程是执行任务的，不是客户端数量
            // 客户端发送连接，服务器获取链接，生产任务

            // 线程池需要把任务push进线程池中去，线程池中的线程执行任务
            //  Task ts(servicefd_,clientip,clientport,service);
            //  threadpool_->push(ts);
            // 5、线程池版本，命令行执行结果服务
            Task ts(servicefd_, clientip, clientport, netcal);
            threadpool_->push(ts);
        }
    }

private:
    int listensockfd_;
    uint16_t port_;
    string ip_;
    threadpool<Task> *threadpool_;
};

int main(int argc, char *argv[])
{
    cout << "hello" << endl;
    if (argc != 2 && argc != 3)
    {
        cout << "failed" << endl;
        exit(3);
    }
    string ip;
    if (argc == 3)
    {
        ip = argv[2];
    }
    uint16_t port = atoi(argv[1]);
    tcp svr(port, ip);
    svr.init();
    svr.loop();
    return 0;
}